/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006-2008 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.toolkit.impl;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.File;
import java.io.IOException;
import java.util.Map;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;

import who.tmap.bo.MultiLayerThematicMap;
import who.tmap.bo.ThematicMap;
import who.tmap.bo.ThematicMapLayer;
import who.tmap.toolkit.MappingToolKit;
import who.tmap.toolkit.MultiLayerThematicMapToolKit;
import who.tmap.toolkit.shape.MapShape;
import who.tmap.toolkit.shape.MapShapes;
import who.tmap.toolkit.shape.annotation.BackgroundImageShape;
import who.tmap.toolkit.shape.impl.MapShapeFactory;

import com.sun.image.codec.jpeg.ImageFormatException;

public class MultiLayerThematicMapToolKitImpl implements MultiLayerThematicMapToolKit {
	
    private static Logger logger = Logger.getLogger(MultiLayerThematicMapToolKitImpl.class);	
    private static final Double MARGIN =5.0;
    private static final Color DEFAULT_BACKGROUND_COLOR =Color.WHITE;
    
    private static final MapShapeFactory maf =MapShapeFactory.getInstance();
	
	private MappingToolKit mtk;
	private Container container;
	private MultiLayerThematicMap mltm;
	
	public void repaintThematicMap( Graphics2D g, PageFormat pageFormat, Integer width, Integer height ) throws Exception {
		
		AffineTransform origAt =(AffineTransform)g.getTransform().clone();
			
		mtk = (MappingToolKit)MappingToolKitFactory.getInstance().getGraphicToolKit();
    	
		mtk.setContainer( container );
		
		ThematicMapLayer firstVisibleLayer =null;
		
		BackgroundImageShape bisSav =(BackgroundImageShape)maf.getGraphic( MapShapes.BACKGROUND_IMAGE );
		
		for( int ct =0; ct < mltm.getLayers().size(); ct++ ) {
			
			ThematicMapLayer layer =mltm.getLayers().get( ct );

			if ( layer.isVisible() ) {
				
				Color bkColorSav =layer.getThematicMap() != null && layer.getThematicMap().getAnnotations() != null 
										? layer.getThematicMap().getAnnotations().getBackgroundColor()
										: DEFAULT_BACKGROUND_COLOR;

				// Use background color of the 1st visible layer
				if ( firstVisibleLayer != null ) {
					layer.getThematicMap().getAnnotations().setBackgroundColor( 
							firstVisibleLayer.getThematicMap().getAnnotations().getBackgroundColor() );
					
					mtk.getTitleShape().setPt( null );
					mtk.getLegendShape().setPt( null );					
				} 
			
				ThematicMap thematicMap =layer.getThematicMap();
				mtk.setThematicMap( thematicMap );
				
				if ( thematicMap.getBaseMap() != null ) {
				
					double minX =thematicMap.getBaseMap().getMinX();
					double minY =thematicMap.getBaseMap().getMinY();
					double maxX =thematicMap.getBaseMap().getMaxX();
					double maxY =thematicMap.getBaseMap().getMaxY();
					
					// Add some margin
		    		mtk.setMinX( minX - ( ( Math.abs( maxX - minX ) * MARGIN ) / 100 ) );
		    		mtk.setMaxX( maxX + ( ( Math.abs( maxX - minX ) * MARGIN ) / 100 ) );
		    		
		    		// Top of the screen
		    		mtk.setMaxY( maxY + ( ( Math.abs( maxY - minY ) * MARGIN ) / 100 ) );
		    		mtk.setMinY( minY - ( ( Math.abs( maxY - minY ) * MARGIN ) / 100 ) );                
			        
					mtk.addShapes( thematicMap.getBaseMap().getShapes() );
				}

				// Use background image of the 1st visible layer only
				if ( firstVisibleLayer != null ) {
					bisSav.assignValues( mtk.getBackgroundImage() );
					mtk.getBackgroundImage().setImage(null);
				}

				Map<MapShape, Object> data = new CartographicDisplayHelper().assignValues( mtk, 
																	 thematicMap.getData(), 
																	 thematicMap.getCartographicDisplay() );
				
				thematicMap.getCartographicDisplay().setPolygonAssociatedData( data );					
		        
				if ( width == null && height == null ) {
					// Process display factor based on shape file import
					mtk.processMapFactor();
				} else {
					mtk.processMapFactor( width, height );
				}
				
				mtk.setMapVisible( layer.isBaseMapVisible() );
				mtk.setCartographicDisplayVisible( layer.isCdVisible() );
				mtk.setAnnotationsVisible( layer.isAnnotationsVisible() );

				mtk.paintThematicMap( g, null, pageFormat );
				
				if ( firstVisibleLayer != null ) {
					layer.getThematicMap().getAnnotations().setBackgroundColor( bkColorSav );
					mtk.getBackgroundImage().assignValues( bisSav );
				} else {
					firstVisibleLayer =layer;
				}
				
				// Reset Graphics2D context
				g.setTransform( origAt );
			}
		}
	
	}
	
	public void repaintThematicMap( Graphics2D g, PageFormat pageFormat ) throws Exception {
		repaintThematicMap( g, pageFormat, null, null );
	}
	
	private ThematicMap getFirstVisibleLayer() {
		ThematicMap thematicMap =null;
		
		for( int ct =0; ct < mltm.getLayers().size(); ct++ ) {

			ThematicMapLayer layer =mltm.getLayers().get( ct );
			
			if ( layer.isVisible() ) {
				thematicMap =layer.getThematicMap();
				break;
			}
			
		}
		
		return thematicMap;
	}
	
    public BufferedImage retrieveThematicMapImage( Integer width, Integer height ) throws Exception {
		BufferedImage bi = new BufferedImage( width, height, BufferedImage.TYPE_INT_RGB );
	    Graphics2D g2bi =(Graphics2D)bi.createGraphics();

	    ThematicMap thematicMap =getFirstVisibleLayer();
	    g2bi.setBackground( thematicMap != null ? thematicMap.getAnnotations().getBackgroundColor() : DEFAULT_BACKGROUND_COLOR );
	    g2bi.setColor( thematicMap != null ? thematicMap.getAnnotations().getBackgroundColor() : DEFAULT_BACKGROUND_COLOR );

	    Rectangle rect =new Rectangle( new Dimension( bi.getWidth(), bi.getHeight() ) );

	    g2bi.draw( rect );
	    g2bi.fill( rect );
	    
	    g2bi.setColor( Color.BLACK );	    
	    
	    repaintThematicMap( g2bi, null, width, height );
	    
	    g2bi.dispose();
	    
	    return bi;
    }	
	
	public void clipboardCopy( Integer width, Integer height ) throws Exception {
		Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
		
		BufferedImage img =retrieveThematicMapImage( width, height );
		
		clipboard.setContents( new ClipboardImage( img ), this );
	}

	public void lostOwnership( Clipboard clipboard, Transferable contents ) {
		
	}
	
	public int print( Graphics graphics, PageFormat pageFormat, int pageIndex ) throws PrinterException {
		if ( pageIndex == 0 ) {		
			try {
				repaintThematicMap( (Graphics2D)graphics, pageFormat );
			} catch( Exception e ) {
				throw new PrinterException( e.toString() );
			}
			
			return Printable.PAGE_EXISTS;					
		} else {
			return Printable.NO_SUCH_PAGE;
		}
	}
	
	public boolean saveThematicMapAsBitmap( File file, String extension, 
											int width, int height ) throws ImageFormatException, IOException {
    	
    	String fileName =file.getAbsolutePath();
    	if ( file.getName().indexOf( '.' ) != -1 ) {
	    	int pos =fileName.lastIndexOf('.');
	    	if ( pos != -1 ) {
	    		fileName =fileName.substring( 0, pos );
	    	}
    	}
    	
    	fileName =fileName + "." + extension;
    	
    	boolean success =false;
    	
    	try {
    		BufferedImage img =retrieveThematicMapImage( width, height );
    		
    		/*if ( removeBlankEdges ) {
    			img =removeBlankEdges( img );
    		}*/
    	
    		success =ImageIO.write( img, extension, new File( fileName ) );
    	} catch( Exception e ) {
    		logger.error( e );
    	}
    	
    	/*logger.error( "file=" + file.toString() + ", extension=" + extension 
    				+ ", width=" + width + ", height=" + height
    				+ ", filename=" + fileName + ", success=" + success
    				);*/

    	return success;
    }	
	
	public void setContainer( Container container ) {
		this.container =container;
	}
	
	public void setMultiLayerThematicMap( MultiLayerThematicMap mltm ) {
		this.mltm =mltm;
	}

}
